/*
    Project:        Home Power Control
    File:           HPCLogger.h
    Author:         christian@karunamusic.at
    Date:           01.2015
    Description:    Basic implementation of a logger-class to send logmessages with
                    various loglevels via signals to all connected subscribers.
 */

#ifndef HPCLOGGER_H
#define HPCLOGGER_H

#include <QTextStream>
#include <QString>
#include <QMutex>
#include <exception>

/**
 * \brief Macro to print log messages.
 * Example of usage of the HPCLogger:
 *	    LOG(info, "hello " << "world");
 */
#define LOG(l,m) \
{ \
    QString __debug_string__; \
    QTextStream __debug_stream__(&__debug_string__); \
    __debug_stream__ << m; \
    __debug_stream__.flush(); \
    HPCLogger::getInstance()->print(l,QString(__FILE__),__LINE__,__debug_string__); \
}

/**
 * \brief Macro to print log messages (level 'trace') and throw an exception with the same message.
 */
#define THROW(m) \
{ \
    QString __debug_string__; \
    QTextStream __debug_stream__(&__debug_string__); \
    __debug_stream__ << m; \
    __debug_stream__.flush(); \
    HPCLogger::getInstance()->print(trace,QString(__FILE__),__LINE__,__debug_string__); \
    throw std::runtime_error(__debug_string__.toStdString()); \
}

/**< Enum with loglevels. */
typedef enum
{
    disabled = 0,
    fatal,
    error,
    warning,
    info,
    debug,
    trace
} TLogLevel;

/** \brief Operator to stream the loglevel as string to console or file.
 *
 * \param dest the ostream.
 * \param msg the HPCLogMessage.
 * \return the ostream.
 *
 */
std::ostream& operator << ( std::ostream& dest, const TLogLevel lv );

class HPCLogMessage : public QObject
{
    Q_OBJECT
public:
    /** \brief Constructor with all arguments from HPCLogger::print.
     *
     * \param loglevel the loglevel - see enum above.
     * \param sourceFile the filename - set with __FILE__ in the LOG macro.
     * \param codeLine the linenumber - set with __LINE__ in the LOG macro.
     * \param message the string with the logmessage.
     */
    HPCLogMessage( const TLogLevel     logLevel,
                   const QString&      sourceFile,
                   const unsigned int  codeLine,
                   const QString&      message );
    /** \brief Destructor.
     */
    virtual ~HPCLogMessage() {}

    /**< get the loglevel */
    inline TLogLevel LogLevel() const
        { return m_LogLevel; }
    /**< get the timestamp */
    inline QString TimeStamp() const
        { return m_TimeStamp; }
    /**< get the sourcefilename */
    inline QString SourceFile() const
        { return m_SourceFile; }
    /**< get the linenumber in the file */
    inline unsigned int CodeLine() const
        { return m_CodeLine; }
    /**< get the logmessage */
    inline QString Message() const
        { return m_Message; }

private:
    TLogLevel    m_LogLevel;
    QString      m_TimeStamp;
    QString      m_SourceFile;
    unsigned int m_CodeLine;
    QString      m_Message;
};

/** \brief Operator to stream the logmessage to console or file.
 *
 * \param dest the ostream.
 * \param msg the HPCLogMessage.
 * \return the ostream.
 *
 */
std::ostream& operator << ( std::ostream& dest, const HPCLogMessage& msg );

class HPCLogger : public QObject
{
    Q_OBJECT
public:
    explicit HPCLogger();
    virtual ~HPCLogger();

    /** \brief Get the static instance of the logger.
     *
     * \return HPCLogger*
     */
    static HPCLogger* getInstance();

    /** \brief Get the loglevel as string.
     *
     * \param loglevel the loglevel - see enum above.
     * \return loglevel as QString.
     */
    static QString LogLevelToString( TLogLevel logLevel );

    /** \brief Get the loglevel from a string.
     *
     * \param loglevel as QString.
     * \return the loglevel - see enum above.
     */
    static TLogLevel StringToLogLevel( const QString& logLevel );

    /** \brief send an event with the logmessage to all subscribed targets.
     *
     * \param loglevel the loglevel - see enum above.
     * \param sourceFile the filename - set with __FILE__ in the LOG macro.
     * \param codeLine the linenumber - set with __LINE__ in the LOG macro.
     * \param message the string with the logmessage.
     *
     */
    void print( const TLogLevel	    logLevel,
                const QString&      sourceFile,
                const unsigned int 	codeLine,
                const QString&      message );

signals:
    void LogEvent(const HPCLogMessage& msg);

public slots:

private:
    static HPCLogger* m_Log; /**< Pointer to the unique HPCLogger. */
    static QMutex m_Mutex; /**< Lock for mutual exclusion between different threads. */
    static QMap<QString,TLogLevel> m_LogLevels; /**< String to TLogLevel map */
};

#endif // HPCLOGGER_H
